﻿// Copyright 2008 Chris Varley (http://www.contractdriven.net/)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Runtime.Remoting.Messaging;
using System.Security.Permissions;

namespace ContractDriven.Aspects
{

	/// <summary>
	/// Implements a message sink to intercept method calls.
	/// The sink is applied to a class using the .NET Remoting subsystem
	/// After each method is called, we intercept the call and check any Invariant Assertions.
	/// </summary>
    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
	public class InvariantAspect : IMessageSink
    {
        private readonly IMessageSink _next;
		private readonly MarshalByRefObject _this;

        internal InvariantAspect(MarshalByRefObject o, IMessageSink next)
        {
        	_this = o;
            _next = next;
        }

        #region IMessageSink Members
        
        public IMessageSink NextSink
        {
            get { return _next; }
        }

        
        public IMessage SyncProcessMessage(IMessage msg)
        {
			//process the original method.
			
				IMessage returnMethod =_next.SyncProcessMessage(msg);
				
				//check any invariants
				DoInvariantCheck(msg);

				return returnMethod;
        }

        [SecurityPermission( SecurityAction.LinkDemand, Flags= SecurityPermissionFlag.Infrastructure)]
        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            throw new InvalidOperationException();
        }

        #endregion


		private void DoInvariantCheck(IMessage msg)
		{
            var call = msg as IMethodMessage;

			// We only want to process method calls
			if (!(call is IMethodMessage)) return;

			
			if (call.MethodName == "Dispose")
			{
				Contract.Unload(_this);
			}
			else
			{
				//Check any invariants on the object.
				Contract.CheckInvariants(_this);
			}
		}

    }


}
